﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Remotify.AzureService;
using Microsoft.Phone.Shell;
using System.IO;
using Microsoft.Xna.Framework.Audio;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Microsoft.Xna.Framework;
using System.Threading;
using Hawaii.Services.Speech.Recognition;
using System.Collections;

namespace Remotify
{
  public class DeviceInfo
  {
    public DeviceInfo()
    { }

    public DeviceInfo(string deviceID, string deviceName)
    {
      DeviceID = deviceID;
      DeviceName = deviceName;
    }

    public string DeviceName
    { get; set; }

    public string DeviceID
    { get; set; }

    public override string ToString()
    {
      return DeviceName.ToString();
    }
  }
  
  public partial class MainPage : PhoneApplicationPage
  {
    public static string myDeviceID = "1";
    private DeviceInfo controlledDevice = new DeviceInfo();
    private AzureService.HelperServiceClient helperServiceClient = new AzureService.HelperServiceClient();
    private const string HostName = "stt.hawaii-services.net";

    private delegate void SetRecognizedTextDelegate(List<string> recognizedTexts);
    private delegate void SetGrammarsDelegate(List<string> domains);

    private Microphone MicroPhone { get; set; }
    private byte[] AudioBuffer { get; set; }
    private MemoryStream AudioStream { get; set; }
    private SoundEffectInstance SoundInstance { get; set; }
    private bool IsSoundPlaying { get; set; }


    // Constructor
    public MainPage()
    {
      InitializeComponent();
      helperServiceClient.CreateQueueCompleted += new EventHandler<CreateQueueCompletedEventArgs>(helperServiceClient_createQueueCompleted);
      helperServiceClient.SendMessageCompleted += new EventHandler<SendMessageCompletedEventArgs>(helperServiceClient_sendMessageCompleted);
      helperServiceClient.GetMessageCompleted += new EventHandler<GetMessageCompletedEventArgs>(helperServiceClient_getMessageCompleted);
      helperServiceClient.DisplayTableCompleted += new EventHandler<DisplayTableCompletedEventArgs>(helperServiceClient_DisplayTableCompleted);
      helperServiceClient.CreateQueueAsync("requestqueue");
      this.AudioStream = new MemoryStream();
      this.MicroPhone = Microphone.Default;
      this.IsSoundPlaying = false;

      // Timer to simulate the XNA Framework game loop (Microphone is 
      // from the XNA Framework). We also use this timer to monitor the 
      // state of audio playback so we can update the UI appropriately.
      DispatcherTimer dispatchTimer = new DispatcherTimer();
      dispatchTimer.Interval = TimeSpan.FromMilliseconds(33);
      dispatchTimer.Tick += new EventHandler(DispatcherTimer_Tick);
      dispatchTimer.Start();

      // Event handler for getting audio data when the buffer is full
      this.MicroPhone.BufferReady += new EventHandler<EventArgs>(Microphone_BufferReady);
    }

    void helperServiceClient_sendMessageCompleted(object sender, SendMessageCompletedEventArgs e)
    {
      string respose = string.Empty;
      if (e.Error == null)
      {
        //textBlock1.Text = textBlock1.Text + "\nMessage sent!";
        lbFeedback.Items.Add("Message sent!");
        lbFeedback.Items.Add("Waiting for response...");
        helperServiceClient.GetMessageAsync(myDeviceID, "responsequeue", false);
      }
      else
      {
        lbFeedback.Items.Add("Error: " + e.Error.Message);
      }
    }

    void helperServiceClient_getMessageCompleted(object sender, GetMessageCompletedEventArgs e)
    {
      if (e.Result == "")
      {
        helperServiceClient.GetMessageAsync(myDeviceID, "responsequeue", false);
        Thread.Sleep(500);
      }
      else
      {
        lbFeedback.Items.Add("Response: " + e.Result);
      }
    }

    void helperServiceClient_createQueueCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
      
    }

    void helperServiceClient_DisplayTableCompleted(object sender, DisplayTableCompletedEventArgs e)
    {
      lbDevices.Items.Clear();
      if (e.Error == null)
      {
        if (e.Result.Length == 0)
          lbDevices.Items.Add("No devices listening.");
        else
        {
          AzureService.DeviceInfo[] deviceList = e.Result;
          lbDevices.Items.Add("Found " + e.Result.Length.ToString() + " devices listening.");
          lbDevices.Items.Add(" ");
          foreach (AzureService.DeviceInfo deviceEntry in deviceList)
          {
            DeviceInfo device = new DeviceInfo(deviceEntry.DeviceID, deviceEntry.DeviceName);
            //device.DeviceID = ;
            //device.DeviceName = ;
            lbDevices.Items.Add(device);
          }
        }
      }
      else
      {
        lbFeedback.Items.Add("Response: " + e.Result);
      }
    }

    private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
    {

    }

    private void btnGo_Click(object sender, RoutedEventArgs e)
    {
      if (controlledDevice == null)
      {
        lbFeedback.Items.Add("Please choose a device first.");
        return;
      }
      lbFeedback.Items.Add("Sending command: " + tbAddressBar.Text);
      string command = tbAddressBar.Text;
      helperServiceClient.SendMessageAsync(controlledDevice.DeviceID, "requestqueue", command);
    }

    private void btnChooseDevice_Click(object sender, RoutedEventArgs e)
    {
      lbDevices.Visibility = System.Windows.Visibility.Visible;
      lbDevices.Items.Add("Retrieving device list... ");
      helperServiceClient.DisplayTableAsync("WP7DeviceTable");
    }

    #region Functions for Recording 

    private void RecordButton_Click(object sender, EventArgs e)
    {
      lbFeedback.Items.Clear();
      lbFeedback.Items.Add("Recording voice...");
      // Get audio data in 1/2 second chunks
      this.MicroPhone.BufferDuration = TimeSpan.FromMilliseconds(250);

      // Allocate memory to hold the audio data
      this.AudioBuffer = new byte[this.MicroPhone.GetSampleSizeInBytes(this.MicroPhone.BufferDuration)];

      // Set the stream back to zero in case there is already something in it
      this.AudioStream.SetLength(0);

      // Start recording
      this.MicroPhone.Start();

      SetButtonStates(false, true, false, false);
    }

    private void StopButton_Click(object sender, EventArgs e)
    {
      lbFeedback.Items.Add("Voice recorded.");
      if (this.MicroPhone.State == MicrophoneState.Started)
      {
        // In RECORD mode, user clicked the 
        // stop button to end recording
        this.MicroPhone.Stop();
      }
      else if (this.SoundInstance.State == SoundState.Playing)
      {
        // In PLAY mode, user clicked the 
        // stop button to end playing back
        this.SoundInstance.Stop();
      }

      SetButtonStates(true, false, true, true);
    }

    private void PlayButton_Click(object sender, EventArgs e)
    {
      if (this.AudioStream.Length > 0)
      {
        // Update the UI to reflect that sound is playing
        SetButtonStates(false, true, false, false);

        // Play the audio in a new thread so the UI can update.
        Thread soundThread = new Thread(new ThreadStart(PlaySound));
        soundThread.Start();
      }
    }

    private void PlaySound()
    {
      // Play audio using SoundEffectInstance so we can monitor it's State 
      // and update the UI in the dt_Tick handler when it is done playing.
      SoundEffect sound = new SoundEffect(this.AudioStream.ToArray(), this.MicroPhone.SampleRate, AudioChannels.Mono);
      this.SoundInstance = sound.CreateInstance();
      this.IsSoundPlaying = true;
      this.SoundInstance.Play();
    }

    private void SetButtonStates(bool recordEnabled, bool stopEnabled, bool playEnabled, bool sendEnabled)
    {
      (ApplicationBar.Buttons[0] as ApplicationBarIconButton).IsEnabled = recordEnabled;
      (ApplicationBar.Buttons[1] as ApplicationBarIconButton).IsEnabled = stopEnabled;
      (ApplicationBar.Buttons[2] as ApplicationBarIconButton).IsEnabled = playEnabled;
      (ApplicationBar.Buttons[3] as ApplicationBarIconButton).IsEnabled = sendEnabled;
    }

    private void RecognizeButton_Click(object sender, EventArgs e)
    {
      SpeechRecognitionClient service = new SpeechRecognitionClient(MainPage.HostName, "12345");
      service.SpeechRecognitionCompleted += OnSpeechRecognitionCompleted;
      service.RecognizeSpeechAsync(this.AudioStream.ToArray());
      //service.RecognizeSpeechAsync(GetAudioBuffer1FromResource());
    }

    private void SetFeedbackListBox(List<string> recognitionResultStrings)
    {
      lbFeedback.Items.Clear();
      if (recognitionResultStrings != null)
      {
        recognitionResultStrings.ForEach((item) => this.lbFeedback.Items.Add(item));
      }
      else
      {
        this.lbFeedback.Items.Add("Sorry, unable to recognize speech");
      }
    }

    private void OnSpeechRecognitionCompleted(object sender, SpeechRecognitionCompletedEventArgs e)
    {
      if (!e.IsErrored)
      {
        this.lbFeedback.Dispatcher.BeginInvoke(new SetRecognizedTextDelegate(SetFeedbackListBox), e.RecognizedTexts);
      }
      else
      {
        System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => { MessageBox.Show(e.ErrorException.Message); });
      }
    }

    private void ClearAllButton_Click(object sender, RoutedEventArgs e)
    {
      this.lbFeedback.Items.Clear();
      tbAddressBar.Text = "";
    }
    private void RecognizedStringListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      if (e.AddedItems == null)
      {
        return;
      }
      else if (e.AddedItems.Count == 0)
      {
        return;
      }
      else
      {
        IList selectedItems = e.AddedItems;
        tbAddressBar.Text = selectedItems.OfType<string>().FirstOrDefault();

        //NavigationService.Navigate(new Uri("/RecognizedSpeechPage.xaml", UriKind.Relative));

        //RecognizedStringListBox.SelectedItem = null;
      }
    }

    private void DeviceInfoListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      if (lbDevices.SelectedItem == null) return;
      controlledDevice = (DeviceInfo)lbDevices.SelectedItem;
      tbChosenDevice.Text = "Controlling device: " + controlledDevice.DeviceName.ToString();
      //lbDevices.Items.Clear();
      lbDevices.Visibility = System.Windows.Visibility.Collapsed;
    }

    void Microphone_BufferReady(object sender, EventArgs e)
    {
      // Retrieve audio data
      this.MicroPhone.GetData(this.AudioBuffer);

      // Store the audio data in a stream
      this.AudioStream.Write(this.AudioBuffer, 0, this.AudioBuffer.Length);
    }

    void DispatcherTimer_Tick(object sender, EventArgs e)
    {
      try
      {
        FrameworkDispatcher.Update();
      }
      catch
      {
      }

      if (true == this.IsSoundPlaying)
      {
        if (this.SoundInstance.State != SoundState.Playing)
        {
          // Audio has finished playing
          this.IsSoundPlaying = false;

          // Update the UI to reflect that the 
          // sound has stopped playing
          SetButtonStates(true, false, true, true);
        }
      }
    }

    #endregion



    public string deviceID { get; set; }
  }
}